package labox.innovation.gameserver.model.itemcontainer;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javolution.util.FastList;
import labox.innovation.config.Config;
import labox.innovation.db.ItemsDb;
import labox.innovation.gameserver.GameTimeController;
import labox.innovation.gameserver.datatables.ItemTable;
import labox.innovation.gameserver.model.FuzeWorld;
import labox.innovation.gameserver.model.L2ItemInstance;
import labox.innovation.gameserver.model.L2Object;
import labox.innovation.gameserver.model.L2ItemInstance.ItemLocation;
import labox.innovation.gameserver.model.actor.L2Attackable;
import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.templates.item.L2Item;

/**
 * 物品容器基类
 * 
 * @author zjj
 * 
 */
public abstract class ItemContainer {

	protected static final Logger _log = LoggerFactory.getLogger(ItemContainer.class.getName());

	protected final List<L2ItemInstance> _items;

	protected ItemContainer() {
		_items = new FastList<L2ItemInstance>();
	}

	protected abstract L2Character getOwner();

	public abstract ItemLocation getBaseLocation();

	public abstract int getMaxCurrentSoltNums();

	public abstract void setMaxCurrentSoltNums(int _maxCurrentSoltNums);

	/**
	 * 获得一个空的插槽 位置 默认 返回第一个位置
	 * 
	 * @return
	 */
	public int getSlot() {
		return 0;
	}

	public String getName() {
		return "ItemContainer";
	};

	/**
	 * Returns the ownerID of the inventory 物品容器的所有者
	 * 
	 * @return int
	 */
	public long getOwnerId() {
		return getOwner() == null ? 0 : getOwner().getObjectId();
	}

	/**
	 * Returns the quantity of items in the inventory 获得 所有的物品数
	 * 
	 * @return int
	 */
	public int getSize() {
		return _items.size();
	}

	/**
	 * Returns the list of items in inventory
	 * 
	 * @return L2ItemInstance : items in inventory 获得所有的物品
	 */
	public L2ItemInstance[] getItems() {
		return _items.toArray(new L2ItemInstance[_items.size()]);
	}

	/**
	 * 
	 * @return
	 */
	public List<L2ItemInstance> getItemsList() {
		return _items;
	}

	/**
	 * Returns the item from inventory by using its <B>itemId</B><BR>
	 * <BR>
	 * 
	 * @param itemId
	 *            : int designating the ID of the item
	 * @return L2ItemInstance designating the item or null if not found in
	 *         inventory 根据物品id 获得相应的物品 只返回 一个物品。 第一个物品Id为指定Id的
	 */
	public L2ItemInstance getItemByItemId(int itemId) {
		for (L2ItemInstance item : _items)
			if (item != null && item.getItemId() == itemId)
				return item;

		return null;
	}
	
	/**
	 * 尝试 获得一个能 叠加count数量的物品
	 * @param itemId
	 * @param count
	 * @return
	 */
	public L2ItemInstance getItemByItemId( int itemId, int count ){
		
		for (L2ItemInstance item : _items)
			if (item != null && item.getItemId() == itemId && item.canStackable(count) )
				return item;

		return null;
		
	}

	/**
	 * Returns the item's list from inventory by using its <B>itemId</B><BR>
	 * <BR>
	 * 
	 * @param itemId
	 *            : int designating the ID of the item
	 * @return List<L2ItemInstance> designating the items list (empty list if
	 *         not found) 返回所有物品， 物品Id为指定Id的
	 */
	public List<L2ItemInstance> getItemsByItemId(int itemId) {
		List<L2ItemInstance> returnList = new FastList<L2ItemInstance>();
		for (L2ItemInstance item : _items) {
			if (item != null && item.getItemId() == itemId) {
				returnList.add(item);
			}
		}

		return returnList;
	}

	/**
	 * Returns the item from inventory by using its <B>itemId</B><BR>
	 * <BR>
	 * 
	 * @param itemId
	 *            : int designating the ID of the item
	 * @param itemToIgnore
	 *            : used during a loop, to avoid returning the same item
	 * @return L2ItemInstance designating the item or null if not found in
	 *         inventory 返回一个物品。 第一个物品Id为指定Id，且不是这个被忽视的物品
	 */
	public L2ItemInstance getItemByItemId(int itemId, L2ItemInstance itemToIgnore) {
		for (L2ItemInstance item : _items)
			if (item != null && item.getItemId() == itemId && !item.equals(itemToIgnore))
				return item;

		return null;
	}

	/**
	 * Returns item from inventory by using its <B>objectId</B>
	 * 
	 * @param objectId
	 *            : int designating the ID of the object
	 * @return L2ItemInstance designating the item or null if not found in
	 *         inventory 根据物品实例Id 返回一个物品 物品实例Id 是唯一的 因此只会返回一个
	 */
	public L2ItemInstance getItemByObjectId(long objectId) {
		for (L2ItemInstance item : _items) {
			if (item == null)
				continue;

			if (item.getObjectId() == objectId)
				return item;
		}
		return null;
	}

	/**
	 * Gets count of item in the inventory
	 * 
	 * @param itemId
	 *            : Item to look for
	 * @param enchantLevel
	 *            : enchant level to match on, or -1 for ANY enchant level
	 * @return int corresponding to the number of items matching the above 返回背包里
	 *         指定物品Id的叠加数量，
	 */
	public int getInventoryItemCount(int itemId) {
		int count = 0;

		for (L2ItemInstance item : _items)
			if (item.getItemId() == itemId)
				if (!item.isEquipped())
					count += item.getCount();

		return count;
		
	}

	/**
	 * Adds item to inventory
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param item
	 *            : L2ItemInstance to be added
	 * @param actor
	 *            : L2PcInstance Player requesting the item add
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the new item or the updated item
	 *         in inventory 向背包中 增加物品
	 */
	public L2ItemInstance addItem(String process, L2ItemInstance item, FuzePcInstance actor, L2Object reference) {
		// 是否物品已存在 物品容器中
		L2ItemInstance olditem = getItemByItemId(item.getItemId());

		int count = item.getCount();
		// If stackable item is found in inventory just add to current quantity
		// 如果物品已存在，且物品可叠加 那么叠加
		if (olditem != null && olditem.canStackable(count) && process != "Restore" ) {
			olditem.changeCount(process, count, actor, reference);
			olditem.setLastChange(L2ItemInstance.MODIFIED);

			// And destroys the item
			ItemTable.getInstance().destroyItem(process, item, actor, reference);
			// item.updateDatabase();
			item = olditem;

			// Updates database
			// if (item.getItemId() == 57 && count < 10000 *
			// Config.RATE_DROP_ADENA) {
			// Small adena changes won't be saved to database all the time
			// if (GameTimeController.getGameTicks() % 5 == 0)
			// item.updateDatabase();
			// } else
			// item.updateDatabase();
		}
		// If item hasn't be found in inventory, create new one
		// 如果物品不在背包中，或者 物品不可叠加。 那么增加这个物品到物品容器中
		else {
			item.setOwnerId(process, getOwnerId(), actor, reference);
			int solt = getSlot();
			if( solt < 0 ){
				ItemTable.getInstance().destroyItem( "背包已经满"+process, item , actor, reference );
				return null;
			}
			item.setLocation(getBaseLocation(), solt );
			item.setLastChange((L2ItemInstance.ADDED));

			// Add item in inventory
			addItem(item);

			// Updates database
			// item.updateDatabase();
		}
		if (item != null && actor != null)
			actor.notifyQuestEventAddItem(item);// 通知添加物品事件
		return item;
	}

	/**
	 * Adds item to inventory
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param itemId
	 *            : int Item Identifier of the item to be added
	 * @param count
	 *            : int Quantity of items to be added
	 * @param actor
	 *            : L2PcInstance Player requesting the item add
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the new item or the updated item
	 *         in inventory
	 */
	public L2ItemInstance addItem(String process, int itemId, int count, FuzePcInstance actor, L2Object reference) {
		// 是否物品已存在 物品容器中
		L2ItemInstance item = getItemByItemId(itemId, count );
		//如果物品存在 且可以叠加， 直接改变以存在物品的数量
		if (item != null ) {
			
			item.changeCount(process, count, actor, reference);
			
			item.setLastChange(L2ItemInstance.MODIFIED);
			
		}
		// 创建新的物品实例
		else {
			// 是否是 怪物
			boolean isMob = reference instanceof L2Attackable;
			//还剩余的需要创建的物品数量
			int hasNums = count;

			for (int i = 0; i < count; i++ ) {
				
				// 获得指定物品id的数据
				L2Item template = ItemTable.getInstance().getTemplate(itemId);
				if (template == null) {
					_log.warn((process + ":" +actor != null ? "[" + actor.getName() + "] " : "") + "Invalid ItemId requested: " + itemId, Config.SERVER_ID);
					return null;
				}

				// 创建一个新的物品实例
				// 如果是怪物 掉落 需要提供怪物的 精英程度 以及 怪物的 等级
				if (isMob) {
					item = ItemTable.getInstance().createItem(process, itemId, (template.isStackable() ? hasNums : 1), actor, ((L2Attackable) reference).getChampion(), ((L2Attackable) reference).getLevel());
				} else {
					item = ItemTable.getInstance().createItem(process, itemId, template.isStackable() ? hasNums : 1, actor, reference);
				}

				if (item == null) {
					_log.error("物品生成为空" + itemId, Config.SERVER_ID);
					return null;
				}

				item.setOwnerId(getOwnerId());
				int index = getSlot();
				if( index < 0 && !item.isMoney() ){
					ItemTable.getInstance().destroyItem( "背包已经满"+process, item , actor, reference );
					return null;
				}
//				System.out.println("物品uid: " + item.getObjectId() + "物品名:" + item.getName() + "插槽位置:" + index);
				item.setLocation(getBaseLocation(),index );
				item.setLastChange(L2ItemInstance.ADDED);

				// Add item in inventory
				addItem(item);
				// Updates database
				// item.updateDatabase();

				// If stackable, end loop as entire count is included in 1
				// instance of item
				// 如果物品时可叠加的 那么一个物品实例就够了
				// 物品不是可叠加的 那么要创建多个物品实例
				if (template.isStackable() || !Config.MULTIPLE_ITEM_DROP)
				{
					//如果是 可叠加物品 那么每次 创建一个物品之后 数量都会减少 已叠加的数量
					hasNums = hasNums - item.getCount();
					
					if( hasNums <= 0 ){
						break;
					}
					
				}

			}
		}
		if (item != null && actor != null)
			actor.notifyQuestEventAddItem(item);// 通知添加物品事件
		return item;
	}

	/**
	 * Adds Wear/Try On item to inventory<BR>
	 * <BR>
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param itemId
	 *            : int Item Identifier of the item to be added
	 * @param actor
	 *            : L2PcInstance Player requesting the item add
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the new weared item 增加 试穿物品
	 *         不会保存到数据库
	 */
	public L2ItemInstance addWearItem(String process, int itemId, FuzePcInstance actor, L2Object reference) {
		// Surch the item in the inventory of the player
		L2ItemInstance item = getItemByItemId(itemId);

		// There is such item already in inventory
		if (item != null)
			return item;

		// Create and Init the L2ItemInstance corresponding to the Item
		// Identifier and quantity
		// Add the L2ItemInstance object to _allObjects of L2world
		item = ItemTable.getInstance().createItem(process, itemId, 1, actor, reference);

		// Set Item Properties
		item.setWear(true); // "Try On(试穿)" Item -> Don't save it in database
		// 试穿物品 不保存到数据库？
		item.setOwnerId(getOwnerId());
		item.setLocation(getBaseLocation());
		item.setLastChange((L2ItemInstance.ADDED));

		// Add item in inventory and equip it if necessary (item location
		// defined)
		addItem(item);

		// Calculate the weight loaded by player
		refreshWeight();

		return item;

	}

	/**
	 * Transfers item to another inventory
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param itemId
	 *            : int Item Identifier of the item to be transfered
	 * @param count
	 *            : int Quantity of items to be transfered
	 * @param actor
	 *            : L2PcInstance Player requesting the item transfer
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the new item or the updated item
	 *         in inventory 移动物品 到另一个物品容器
	 */
	public L2ItemInstance transferItem(String process, long objectId, int count, ItemContainer target, FuzePcInstance actor, L2Object reference) {
		if (target == null) {
			return null;
		}

		L2ItemInstance sourceitem = getItemByObjectId(objectId);

		if (sourceitem == null) {
			return null;
		}

		// 物品的最大叠加数量
		int stackNums = sourceitem.getItem().getMaxStackNums();
		boolean isDestory = false;

		// 查看目标容器中 是否已有这个物品，如果这个物品能叠加的话， 否则设为null
		L2ItemInstance targetitem = sourceitem.isStackable() ? target.getItemByItemId(sourceitem.getItemId(), count) : null;
		synchronized (sourceitem) {
			// check if this item still present in this container
			// 物品是否还在物品容器中
			if (getItemByObjectId(objectId) != sourceitem) {
				return null;
			}

			// Check if requested quantity is available
			if (count > sourceitem.getCount())
				count = sourceitem.getCount();

			// If possible, move entire item object
			// 如果源物品的数量 和 移动的数量一样 那么在源物品容器中删掉这个物品， 在目标容器中加入这个物品
			if (sourceitem.getCount() == count && targetitem == null) {
				removeItem(sourceitem);
				target.addItem(process, sourceitem, actor, reference);
				targetitem = sourceitem;
			} else {
				if (sourceitem.getCount() > count) // If possible, only update
				// counts
				{
					sourceitem.changeCount(process, -count, actor, reference);
				} else
				// Otherwise destroy old item
				{
//					sourceitem.changeCount(process, -count, actor, reference);
					removeItem(sourceitem);
					isDestory = true;
					// TODO : 不摧毁物品， 直接使用原来的物品实例
					// ItemTable.getInstance().destroyItem(process, sourceitem,
					// actor, reference);
				}

				// 当目标容器中存在道具， 且叠加数量没有达到最大数时， 才可以只改变当前物品的数量
				if (targetitem != null && (targetitem.getCount() + count) < stackNums) // 
				{
					targetitem.changeCount(process, count, actor, reference);
					if( isDestory )
						ItemTable.getInstance().destroyItem(process, sourceitem, actor, reference);
					
					System.out.println("sourceitem" + sourceitem.getCount() + "插槽位置:" + sourceitem.get_locData() );
					
				} else
				// Otherwise add new item
				{
					
					//如果物品 被完全从这个容器中移除了
					//那么 把此物品实例加入到另一个容器中
					if( isDestory ){
						
						targetitem = target.addItem(process, sourceitem, actor, reference);
						
					}else{
						
						//如果没有完全移除掉这个物品 那么 把这个物品 指定的数量增加到对方容器中
						targetitem = target.addItem(process, sourceitem.getItemId(), count, actor, reference);
//						if( isDestory )
//							ItemTable.getInstance().destroyItem(process, sourceitem, actor, reference);
						
					}
					
//					if (targetitem == null){
//						targetitem = target.addItem(process, sourceitem.getItemId(), count, actor, reference);
//						if( isDestory )
//							ItemTable.getInstance().destroyItem(process, sourceitem, actor, reference);
//					}
//					else
//						targetitem = target.addItem(process, sourceitem, actor, reference);
					
				}
			}
			
			_log.info(process+":" + sourceitem.getName() + "(" + sourceitem.getObjectId() + ")," + count + "从" + getOwner().getName() + "(" + getOwner().getObjectId() + ")" + getBaseLocation() + "到" + target.getOwner().getName() + "(" + target.getOwner().getObjectId() + ")" +  target.getBaseLocation(), sourceitem.getObjectId(), sourceitem.getName() );

		}

		return targetitem;
	}

	/**
	 * Destroy item from inventory and updates database
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param item
	 *            : L2ItemInstance to be destroyed
	 * @param actor
	 *            : L2PcInstance Player requesting the item destroy
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the destroyed item or the updated
	 *         item in inventory 从物品容器中摧毁一个物品， 并更新数据库
	 */
	public L2ItemInstance destroyItem(String process, L2ItemInstance item, FuzePcInstance actor, L2Object reference) {
		return this.destroyItem(process, item, item.getCount(), actor, reference);
	}

	/**
	 * Destroy item from inventory and updates database
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param item
	 *            : L2ItemInstance to be destroyed
	 * @param actor
	 *            : L2PcInstance Player requesting the item destroy
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the destroyed item or the updated
	 *         item in inventory 从物品容器中摧毁一个物品
	 */
	public L2ItemInstance destroyItem(String process, L2ItemInstance item, int count, FuzePcInstance actor, L2Object reference) {
		synchronized (item) {
			// Adjust item quantity
			if (item.getCount() > count) {
				item.changeCount(process, -count, actor, reference);
				item.setLastChange(L2ItemInstance.MODIFIED);

				// don't update often for untraced items
				if (process != null || GameTimeController.getGameTicks() % 10 == 0) {
					// item.updateDatabase();
				}

				refreshWeight();

				return item;
			} else {
				if (item.getCount() < count)
					return null;

				boolean removed = this.removeItem(item);
				if (!removed)
					return null;

				ItemTable.getInstance().destroyItem(process, item, actor, reference);

				// item.updateDatabase();
			}
		}
		return item;
	}

	/**
	 * Destroy item from inventory by using its <B>objectID</B> and updates
	 * database
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param objectId
	 *            : int Item Instance identifier of the item to be destroyed
	 * @param count
	 *            : int Quantity of items to be destroyed
	 * @param actor
	 *            : L2PcInstance Player requesting the item destroy
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the destroyed item or the updated
	 *         item in inventory 摧毁一个物品 由于使用了这个物品
	 */
	public L2ItemInstance destroyItem(String process, long objectId, int count, FuzePcInstance actor, L2Object reference) {
		L2ItemInstance item = getItemByObjectId(objectId);
		if (item == null) {
			return null;
		}
		return this.destroyItem(process, item, count, actor, reference);
	}

	/**
	 * Destroy item from inventory by using its <B>itemId</B> and updates
	 * database
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param itemId
	 *            : int Item identifier of the item to be destroyed
	 * @param count
	 *            : int Quantity of items to be destroyed
	 * @param actor
	 *            : L2PcInstance Player requesting the item destroy
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation
	 * @return L2ItemInstance corresponding to the destroyed item or the updated
	 *         item in inventory 摧毁一个物品 由于使用了这个物品
	 */
	public L2ItemInstance destroyItemByItemId(String process, int itemId, int count, FuzePcInstance actor, L2Object reference) {
		L2ItemInstance item = getItemByItemId(itemId);
		if (item == null) {
			return null;
		}
		return this.destroyItem(process, item, count, actor, reference);
	}

	/**
	 * Destroy all items from inventory and updates database
	 * 
	 * @param process
	 *            : String Identifier of process triggering this action
	 * @param actor
	 *            : L2PcInstance Player requesting the item destroy
	 * @param reference
	 *            : L2Object Object referencing current action like NPC selling
	 *            item or previous item in transformation 销毁所有物品
	 */
	public synchronized void destroyAllItems(String process, FuzePcInstance actor, L2Object reference) {
		for (L2ItemInstance item : _items) {
			if (item != null)
				destroyItem(process, item, actor, reference);
		}
	}

	/**
	 * Get warehouse adena 获得 仓库中的 adena 的数量
	 */
	public int getMoneySilver() {
		int count = 0;

		for (L2ItemInstance item : _items) {
			if (item.getItemId() == 57) {
				count = item.getCount();
				return count;
			}
		}

		return count;
	}

	/**
	 * Adds item to inventory for further adjustments.
	 * 
	 * @param item
	 *            : L2ItemInstance to be added from inventory 增加物品到 物品容器中
	 */
	protected void addItem(L2ItemInstance item) {
		_items.add(item);
	}

	/**
	 * Removes item from inventory for further adjustments.
	 * 
	 * @param item
	 *            : L2ItemInstance to be removed from inventory
	 */
	protected boolean removeItem(L2ItemInstance item) {
		return _items.remove(item);
	}

	/**
	 * Refresh the weight of equipment loaded
	 */
	protected void refreshWeight() {
	}

	/**
	 * 从游戏世界中 删除物品
	 */
	public void deleteMe() {
		for (L2Object o : _items) {
			if (o != null)
				FuzeWorld.getInstance().exitWorld(o);
		}
	}
	
	/**
	 * 从游戏世界中加入物品
	 */
	public void spawnMe() {
		for (L2Object o : _items) {
			if (o != null)
				FuzeWorld.getInstance().enterWorld(o);
		}
	}

	public boolean validateCapacity(int slots) {
		return true;
	}

	public boolean validateWeight(int weight) {
		return true;
	}

}
